#include <math.h>
#include "octree.h"

#define min(x,y) ((x < y) ? x:y)
#define max(x,y) ((x > y) ? x:y)
#define swap(x,y) {x^=y; y^=x; x^=y;}

Octree octree;

Octree::Octree(int _size) 
{
	size = (int) pow(2,_size); 
	root = new octleafempty(0, 0, 0, size-1, size-1, size-1);
}

Octree::~Octree()
{
	printf("deleting <octree at %p>\n", this);
	delete root;
}

void Octree::SetVolume(int state, int x1, int y1, int z1, int x2, int y2, int z2)
{
	if (x1 > x2) swap(x1, x2);
	if (y1 > y2) swap(y1, y2);
	if (z1 > z2) swap(z1, z2);

	/*printf("SetVolume on %p to %i (%i %i %i)-(%i %i %i)\n",
		this, state, x1, y1, z1, x2, y2, z2);*/

	root = root->SetVolume(state, x1,y1,z1, x2,y2,z2, 0);
	//printf("\n");
}

void Octree::Render()
{
	root->Render();
}

void Octree::Print()
{
	root->Print(0);
}

octbranch::octbranch(int _x1, int _y1, int _z1, int _x2, int _y2, int _z2)
{
	x1 = _x1; y1 = _y1; z1 = _z1; x2 = _x2; y2 = _y2; z2 = _z2;
	child[0] = child[1] = child[2] = child[3] = child[4] = child[5] = child[6] = child[7] = 0;
	//printf("[branch %p (%i %i %i)-(%i %i %i)\n", this, x1,y1,z1,x2,y2,z2);
}

octbranch::~octbranch()
{
	//printf("deleting <branch at %p>\n", this);
	for(int i = 0; i < 8; i++)
	if(child[i]) delete child[i];
}

octnode* octbranch::SetVolume(int state, int _x1, int _y1, int _z1, int _x2, int _y2, int _z2, int tabdepth)
{
	/*for(int i = 0; i < tabdepth; i++) printf("  ");
	printf("SetVolume on branch %p to %i (%i %i %i)-(%i %i %i)\n",
		this, state, _x1, _y1, _z1, _x2, _y2, _z2);

	for(int i = 0; i < tabdepth; i++) printf("  ");
	printf("octbranch(%i %i %i %i %i %i)", x1, y1, z1, x2, y2, z2);
	printf(" (%i %i %i %i %i %i %i)", state, _x1, _y1, _z1, _x2, _y2, _z2);*/

	if(_x1 == _x2 || _y1 == _y2 || _z1 == _z2) return this;

	if(x1 == _x1 && y1 == _y1 && z1 == _z1 && x2 == _x2 && y2 == _y2 && z2 == _z2)
	{
		if(state == 1)
		{
	//		printf(" => octleaffilled\n", _x1, _y1, _z1, _x2, _y2, _z2);
		delete this;
			return new octleaffilled(_x1, _y1, _z1, _x2, _y2, _z2);
		}
		else
		{
	//		printf(" => octleafempty\n", _x1, _y1, _z1, _x2, _y2, _z2);
		delete this;
			return new octleafempty(_x1, _y1, _z1, _x2, _y2, _z2);
		}
	}
	else
	{
		//printf("\n");
		int midx, midy, midz;
		midx = (x2 + x1)/2;
		midy = (y2 + y1)/2;
		midz = (z2 + z1)/2;

		tabdepth++;
		child[0] = child[0]->SetVolume(state, min(_x1, midx), min(_y1, midy), min(_z1, midz), 
											  min(midx, _x2), min(midy, _y2), min(midz, _z2), 
											  tabdepth);

		child[1] = child[1]->SetVolume(state, min(_x1, midx), min(_y1, midy), max(_z1, midz), 
											  min(midx, _x2), min(midy, _y2), max(midz, _z2), 
											  tabdepth);

		child[2] = child[2]->SetVolume(state, min(_x1, midx), max(_y1, midy), min(_z1, midz), 
											  min(midx, _x2), max(midy, _y2), min(midz, _z2), 
											  tabdepth);

		child[3] = child[3]->SetVolume(state, min(_x1, midx), max(_y1, midy), max(_z1, midz), 
											  min(midx, _x2), max(midy, _y2), max(midz, _z2), 
											  tabdepth);

		child[4] = child[4]->SetVolume(state, max(_x1, midx), min(_y1, midy), min(_z1, midz), 
											  max(midx, _x2), min(midy, _y2), min(midz, _z2), 
											  tabdepth);

		child[5] = child[5]->SetVolume(state, max(_x1, midx), min(_y1, midy), max(_z1, midz), 
											  max(midx, _x2), min(midy, _y2), max(midz, _z2), 
											  tabdepth);

		child[6] = child[6]->SetVolume(state, max(_x1, midx), max(_y1, midy), min(_z1, midz), 
											  max(midx, _x2), max(midy, _y2), min(midz, _z2), 
											  tabdepth);

		child[7] = child[7]->SetVolume(state, max(_x1, midx), max(_y1, midy), max(_z1, midz), 
											  max(midx, _x2), max(midy, _y2), max(midz, _z2), 
											  tabdepth);

		return this;
	}
}

void octbranch::Render()
{
	for(int i = 0; i < 8; i++)
		child[i]->Render();
}

void octbranch::Print(int tab)
{
	for(int i = 0; i < tab; i++) printf("  ");
	printf("octbranch\n");
	for(int i = 0; i < 8; i++) child[i]->Print(tab+1);
}

octleaffilled::octleaffilled(int _x1, int _y1, int _z1, int _x2, int _y2, int _z2)
{
	x1 = _x1; y1 = _y1; z1 = _z1; x2 = _x2; y2 = _y2; z2 = _z2;
	//printf("[filled %p (%i %i %i)-(%i %i %i)\n", this, x1,y1,z1,x2,y2,z2);
}

octleaffilled::~octleaffilled()
{
	//printf("delete filled leaf\n");
}

octnode* octleaffilled::SetVolume(int state, int _x1, int _y1, int _z1, int _x2, int _y2, int _z2, int tabdepth)
{
	/*for(int i = 0; i < tabdepth; i++) printf("  ");
	printf("SetVolume on leaffilled %p to %i (%i %i %i)-(%i %i %i)\n",
		this, state, _x1, _y1, _z1, _x2, _y2, _z2);

	for(int i = 0; i < tabdepth; i++) printf("  ");
	printf("octleaffilled(%i %i %i %i %i %i)", x1, y1, z1, x2, y2, z2);
	printf(" (%i %i %i %i %i %i %i)", state, _x1, _y1, _z1, _x2, _y2, _z2);*/

	if(_x1 == _x2 || _y1 == _y2 || _z1 == _z2)
	{
		return this;
	}

	if(x1 == _x1 && x2 == _x2 && y1 == _y1 && y2 == _y2 && z1 == _z1 && z2 == _z2)
	{
		if(state == 1)
		{
			//printf("\n");
			return this;
		}
		else 
		{
			//printf(" => octleafempty\n", x1, y1, z1, x2, y2, z2);
			delete this;
			return new octleafempty(_x1, _y1, _z1, _x2, _y2, _z2);
		}
	}

	int midx, midy, midz;
	midx = (x2 + x1)/2;
	midy = (y2 + y1)/2;
	midz = (z2 + z1)/2;

	//printf(" => octbranch\n", x1, y1, z1, x2, y2, z2);
	octbranch* ret = new octbranch(x1, y1, z1, x2, y2, z2);
	ret->child[0] = new octleaffilled(  x1,   y1,   z1, midx, midy, midz);
	ret->child[1] = new octleaffilled(  x1,   y1, midz, midx, midy,   z2);
	ret->child[2] = new octleaffilled(  x1, midy,   z1, midz,   y2, midz);
	ret->child[3] = new octleaffilled(  x1, midy, midz, midx,   y2,   z2);
	ret->child[4] = new octleaffilled(midx,   y1,   z1,   x2, midy, midz);
	ret->child[5] = new octleaffilled(midx,   y1, midz,   x2, midy,   z2);
	ret->child[6] = new octleaffilled(midx, midy,   z1,   x2,   y2, midz);
	ret->child[7] = new octleaffilled(midx, midy, midz,   x2,   y2,   z2);

	delete this;
	return ret->SetVolume(state, _x1, _y1, _z1, _x2, _y2, _z2, tabdepth);
}

void octleaffilled::Render()
{
	//Draw a box
}

void octleaffilled::Print(int tab)
{
	for(int i = 0; i < tab; i++) printf("  ");
	printf("octleaffilled\n");
}

octleafempty::octleafempty(int _x1, int _y1, int _z1, int _x2, int _y2, int _z2)
{
	x1 = _x1; y1 = _y1; z1 = _z1; x2 = _x2; y2 = _y2; z2 = _z2;
	//printf("[empty %p (%i %i %i)-(%i %i %i)\n", this, x1,y1,z1,x2,y2,z2);
}

octleafempty::~octleafempty()
{
	//printf("delete empty leaf\n");
}

octnode* octleafempty::SetVolume(int state, int _x1, int _y1, int _z1, int _x2, int _y2, int _z2, int tabdepth)
{
	/*for(int i = 0; i < tabdepth; i++) printf("  ");
	printf("SetVolume on leafempty %p to %i (%i %i %i)-(%i %i %i)\n",
		this, state, _x1, _y1, _z1, _x2, _y2, _z2);*/

	/*for(int i = 0; i < tabdepth; i++) printf("  ");
	printf("octleafempty(%i %i %i %i %i %i)", x1, y1, z1, x2, y2, z2);
	printf(" (%i %i %i %i %i %i %i)", state, _x1, _y1, _z1, _x2, _y2, _z2);*/

	if(_x1 == _x2 || _y1 == _y2 || _z1 == _z2)
	{
		//printf("\n");
		return this;
	}

	if(x1 == _x1 && x2 == _x2 && y1 == _y1 && y2 == _y2 && z1 == _z1 && z2 == _z2)
	{
		if(state == 1)
		{
	//		printf(" => octleaffilled\n", x1, y1, z1, x2, y2, z2);
			delete this;
			return new octleaffilled(_x1, _y1, _z1, _x2, _y2, _z2);
		}
		else
		{
			return this;
		}
	}

	//printf(" => octbranch\n");
	int midx, midy, midz;
	midx = (x2 + x1)/2;
	midy = (y2 + y1)/2;
	midz = (z2 + z1)/2;

	octbranch* ret = new octbranch(x1, y1, z1, x2, y2, z2);
	ret->child[0] = new octleafempty(  x1,   y1,   z1, midx, midy, midz);
	ret->child[1] = new octleafempty(  x1,   y1, midz, midx, midy,   z2);
	ret->child[2] = new octleafempty(  x1, midy,   z1, midx,   y2, midz);
	ret->child[3] = new octleafempty(  x1, midy, midz, midx,   y2,   z2);
	ret->child[4] = new octleafempty(midx,   y1,   z1,   x2, midy, midz);
	ret->child[5] = new octleafempty(midx,   y1, midz,   x2, midy,   z2);
	ret->child[6] = new octleafempty(midx, midy,   z1,   x2,   y2, midz);
	ret->child[7] = new octleafempty(midx,  midy, midz,  x2,   y2,   z2);

	delete this;
	return ret->SetVolume(state, _x1, _y1, _z1, _x2, _y2, _z2, tabdepth);
}

void octleafempty::Render()
{
	//Draw entities
}

void octleafempty::Print(int tab)
{
	for(int i = 0; i < tab; i++) printf("  ");
	printf("octleafempty\n");
}

int l_setvolume(lua_State* L)
{
	int state = luaL_checkint(L, 1);
	int x1 = luaL_checkint(L, 2);
	int y1 = luaL_checkint(L, 3);
	int z1 = luaL_checkint(L, 4);
	int x2 = luaL_checkint(L, 5);
	int y2 = luaL_checkint(L, 6);
	int z2 = luaL_checkint(L, 7);

	octree.SetVolume(state, x1, y1, z1, x2, y2, z2);

	return 0;
}

int l_printoctree(lua_State *L)
{
	octree.Print();
	return 0;
}

